<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  
  
  <title>一、spring基础介绍 | Hexo</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="第一章    Spring1.1 简单描述 Spring Spring是一个轻量级Java开发框架，目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。 它是一个分层的JavaSE&#x2F;JavaEE full-stack（一站式）轻量级开源框架，为开发Java应用程序提供全面的基础架构支持。Spring负责基础架构，因此Java开发者可以专注于应用程序的开发。 Spring最根本的使命是解决">
<meta property="og:type" content="article">
<meta property="og:title" content="一、spring基础介绍">
<meta property="og:url" content="https://goofyer.gitee.io/notes-on-computer-expertise/2022/01/02/Spring%E5%BC%80%E5%8F%91/%E4%B8%80%E3%80%81spring%E5%9F%BA%E7%A1%80%E4%BB%8B%E7%BB%8D/index.html">
<meta property="og:site_name" content="Hexo">
<meta property="og:description" content="第一章    Spring1.1 简单描述 Spring Spring是一个轻量级Java开发框架，目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。 它是一个分层的JavaSE&#x2F;JavaEE full-stack（一站式）轻量级开源框架，为开发Java应用程序提供全面的基础架构支持。Spring负责基础架构，因此Java开发者可以专注于应用程序的开发。 Spring最根本的使命是解决">
<meta property="og:locale" content="en_US">
<meta property="article:published_time" content="2022-01-02T06:56:00.000Z">
<meta property="article:modified_time" content="2022-02-19T13:41:29.005Z">
<meta property="article:author" content="John Doe">
<meta name="twitter:card" content="summary">
  
    <link rel="alternate" href="/notes-on-computer-expertise/atom.xml" title="Hexo" type="application/atom+xml">
  
  
    <link rel="shortcut icon" href="/notes-on-computer-expertise/favicon.png">
  
  
    
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/typeface-source-code-pro@0.0.71/index.min.css">

  
  
<link rel="stylesheet" href="/notes-on-computer-expertise/css/style.css">

  
    
<link rel="stylesheet" href="/notes-on-computer-expertise/fancybox/jquery.fancybox.min.css">

  
<meta name="generator" content="Hexo 5.4.0"></head>

<body>
  <div id="container">
    <div id="wrap">
      <header id="header">
  <div id="banner"></div>
  <div id="header-outer" class="outer">
    <div id="header-title" class="inner">
      <h1 id="logo-wrap">
        <a href="/notes-on-computer-expertise/" id="logo">Hexo</a>
      </h1>
      
    </div>
    <div id="header-inner" class="inner">
      <nav id="main-nav">
        <a id="main-nav-toggle" class="nav-icon"></a>
        
          <a class="main-nav-link" href="/notes-on-computer-expertise/">Home</a>
        
          <a class="main-nav-link" href="/notes-on-computer-expertise/archives">Archives</a>
        
      </nav>
      <nav id="sub-nav">
        
          <a id="nav-rss-link" class="nav-icon" href="/notes-on-computer-expertise/atom.xml" title="RSS Feed"></a>
        
        <a id="nav-search-btn" class="nav-icon" title="Search"></a>
      </nav>
      <div id="search-form-wrap">
        <form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit">&#xF002;</button><input type="hidden" name="sitesearch" value="https://goofyer.gitee.io/notes-on-computer-expertise"></form>
      </div>
    </div>
  </div>
</header>

      <div class="outer">
        <section id="main"><article id="post-Spring开发/一、spring基础介绍" class="h-entry article article-type-post" itemprop="blogPost" itemscope itemtype="https://schema.org/BlogPosting">
  <div class="article-meta">
    <a href="/notes-on-computer-expertise/2022/01/02/Spring%E5%BC%80%E5%8F%91/%E4%B8%80%E3%80%81spring%E5%9F%BA%E7%A1%80%E4%BB%8B%E7%BB%8D/" class="article-date">
  <time class="dt-published" datetime="2022-01-02T06:56:00.000Z" itemprop="datePublished">2022-01-02</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/notes-on-computer-expertise/categories/spring/">spring</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 class="p-name article-title" itemprop="headline name">
      一、spring基础介绍
    </h1>
  

      </header>
    
    <div class="e-content article-entry" itemprop="articleBody">
      
        <h1 id="第一章-Spring"><a href="#第一章-Spring" class="headerlink" title="第一章    Spring"></a>第一章    Spring</h1><h2 id="1-1-简单描述-Spring"><a href="#1-1-简单描述-Spring" class="headerlink" title="1.1 简单描述 Spring"></a>1.1 简单描述 Spring</h2><ul>
<li>Spring是一个<strong>轻量级</strong>Java开发框架，目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题。</li>
<li>它是一个分层的JavaSE/JavaEE full-stack（一站式）轻量级开源框架，为开发Java应用程序提供全面的基础架构支持。Spring负责基础架构，因此Java开发者可以专注于应用程序的开发。</li>
<li>Spring最根本的使命是解决企业级应用开发的复杂性，即简化Java开发。</li>
<li>Spring有很多丰富的功能，但是这些功能的底层都依赖于它的两个核心特性，也就是依赖注入（dependency injection，DI）和面向切面编程（aspect-oriented programming，AOP）。<h2 id="1-2-简述Spring应用了哪些设计模式"><a href="#1-2-简述Spring应用了哪些设计模式" class="headerlink" title="1.2 简述Spring应用了哪些设计模式"></a>1.2 简述Spring应用了哪些设计模式</h2></li>
<li>答：Spring主要应用了以下<strong>5种</strong>设计模式：<ul>
<li>1.工厂模式：BeanFactory就是简单工厂模式的体现，用来创建对象的实例；</li>
<li>2.单例模式：Bean默认为单例模式。</li>
<li>3.代理模式：Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术；</li>
<li>4.模板方法：用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。</li>
<li>5.观察者模式：定义对象键一种一对多的依赖关系，当一个对象的状态发生改变时，所有依赖于它的对象都会得到通知被制动更新，如Spring中listener的实现–ApplicationListener。<h2 id="1-3-简述SpringContext"><a href="#1-3-简述SpringContext" class="headerlink" title="1.3 简述SpringContext"></a>1.3 简述SpringContext</h2></li>
</ul>
</li>
<li>SpringContenxt 是Spring核心容器，是基本的Spring模块，提供Spring框架的基础功能，BeanFactory 是任何以Spring为基础的应用的核心。Spring框架建立在此模块之上，它使Spring成为一个容器。</li>
<li>Bean 工厂是工厂模式的一个实现，提供了控制反转功能，用来把应用的配置和依赖从真正的应用代码中分离。最常用的就是org.springframework.beans.factory.xml.XmlBeanFactory ，它根据XML文件中的定义加载beans。该容器从XML 文件读取配置元数据并用它去创建一个完全配置的系统或应用。<h2 id="1-4-简述Spring-IOC"><a href="#1-4-简述Spring-IOC" class="headerlink" title="1.4 简述Spring IOC"></a>1.4 简述Spring IOC</h2></li>
<li>答：【重点】IoC (Inversion of Control) 就是控制反转，它把传统上由程序代码直接操控的对象的调用权交给容器，通过容器来实现对象组件的装配和管理。<ul>
<li>所谓的“控制反转”概念就是对组件对象控制权的转移，从程序代码本身转移到了外部容器。<ul>
<li>SpringIOC 负责创建对象<ul>
<li>管理对象（通过依赖注入（DI），</li>
<li>装配对象，</li>
<li>配置对象，</li>
<li>并且管理这些对象的整个生命周期。</li>
</ul>
</li>
</ul>
</li>
<li>控制反转(IoC)的作用——<ul>
<li>1．管理对象的创建和依赖关系的维护。对象的创建并不是一件简单的事，在对象关系比较复杂时，如果依赖关系需要开发人员来维护的话，还是很困难的。</li>
<li>2．解耦，由容器去维护具体的对象。</li>
<li>3．托管了类的产生过程，比如我们需要在类的产生过程中做一些处理，最直接的例子就是代理，如果有容器程序可以把这部分处理交给容器，应用程序则无需去关心类是如何完成代理的。</li>
</ul>
</li>
<li>IOC的优点是——<ul>
<li><ol>
<li>IOC或依赖注入把应用的代码量降到最低</li>
</ol>
</li>
<li><ol>
<li>它使应用容易测试，单元测试不再需要单例和JNDI查找机制</li>
</ol>
</li>
<li><ol>
<li>最小的代价和最小的侵入性使松散耦合得以实现</li>
</ol>
</li>
<li><ol>
<li>IOC容器支持加载服务时的饿汉式初始化和懒加载</li>
</ol>
</li>
</ul>
</li>
<li>Spring中的 IoC 的实现原理就是工厂模式加反射机制。<h2 id="1-5-简述Spring-IOC-提供哪些功能"><a href="#1-5-简述Spring-IOC-提供哪些功能" class="headerlink" title="1.5 简述Spring IOC 提供哪些功能"></a>1.5 简述Spring IOC 提供哪些功能</h2></li>
</ul>
</li>
<li>1.依赖注入<ul>
<li>从 XML 的配置上说，即 ref 标签。对应 SpringRuntimeBeanReference 对象。</li>
<li>对于 IoC 来说，最重要的就是容器。容器管理着 Bean 的生命周期，控制着 Bean 的依赖注入</li>
</ul>
</li>
<li>2.依赖检查</li>
<li>3.自动装配</li>
<li>4.支持集合</li>
<li>5.指定初始化方法和销毁方法</li>
<li>6.支持回调某些方法（但是需要实现 Spring接口，略有侵入）<h2 id="1-6-简述BeanFactory-和-ApplicationContext区别"><a href="#1-6-简述BeanFactory-和-ApplicationContext区别" class="headerlink" title="1.6 简述BeanFactory 和 ApplicationContext区别"></a>1.6 简述BeanFactory 和 ApplicationContext区别</h2></li>
<li>BeanFactory和ApplicationContext是Spring的两大核心接口，都可以做Spring的容器。其中ApplicationContext是BeanFactory的子接口。<h3 id="1-6-1-依赖关系"><a href="#1-6-1-依赖关系" class="headerlink" title="1.6.1 依赖关系"></a>1.6.1 依赖关系</h3></li>
<li>BeanFactory：是Spring里面最下层的接口，包含了各种Bean的定义，读取bean配置文档，管理bean的加载、实例化，控制bean的生命周期，维护bean之间的依赖关系。<ul>
<li>ApplicationContext接口作为BeanFactory的派生，除了提供BeanFactory所具有的功能外，还提供了更完整的框架功能：<ul>
<li>继承MessageSource，因此支持国际化。</li>
<li>统一的资源文件访问方式。</li>
<li>提供在监听器中注册bean的事件。</li>
<li>同时加载多个配置文件。</li>
<li>载入多个（有继承关系）上下文 ，使得每一个上下文都专注于一个特定的层次，比如应用的web层。<h3 id="1-6-2-加载方式"><a href="#1-6-2-加载方式" class="headerlink" title="1.6.2 加载方式"></a>1.6.2 加载方式</h3></li>
</ul>
</li>
</ul>
</li>
<li>BeanFactroy采用的是延迟加载形式来注入Bean的，即只有在使用到某个Bean时(调用getBean())，才对该Bean进行加载实例化。这样，我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入，BeanFacotry加载后，直至第一次使用调用getBean方法才会抛出异常。</li>
<li>ApplicationContext，它是在容器启动时，一次性创建了所有的Bean。这样，在容器启动时，我们就可以发现Spring中存在的配置错误，这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预加载入所有的单实例Bean，通过预载入单实例bean ,确保当你需要的时候，你就不用等待，因为它们已经创建好了。</li>
<li>相对于基本的BeanFactory，ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多时，程序启动较慢。<h3 id="1-6-3-创建方式"><a href="#1-6-3-创建方式" class="headerlink" title="1.6.3 创建方式"></a>1.6.3 创建方式</h3></li>
<li>BeanFactory通常以编程的方式被创建，ApplicationContext还能以声明的方式创建，如使用ContextLoader。<h3 id="1-6-4-注册方式"><a href="#1-6-4-注册方式" class="headerlink" title="1.6.4 注册方式"></a>1.6.4 注册方式</h3></li>
<li>BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用，但两者之间的区别是：BeanFactory需要手动注册，而ApplicationContext则是自动注册。<h2 id="1-7-简述BeanFactory和FactoryBean的区别"><a href="#1-7-简述BeanFactory和FactoryBean的区别" class="headerlink" title="1.7 简述BeanFactory和FactoryBean的区别"></a>1.7 简述BeanFactory和FactoryBean的区别</h2></li>
<li>BeanFactory是个Factory，也就是IOC容器或对象工厂，FactoryBean是个Bean。在Spring中，所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。对FactoryBean而言，这个Bean不是简单的Bean，而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。<h2 id="1-8-Spring循环依赖问题解决原理"><a href="#1-8-Spring循环依赖问题解决原理" class="headerlink" title="1.8 Spring循环依赖问题解决原理"></a>1.8 Spring循环依赖问题解决原理</h2></li>
<li>Spring通过三级缓存解决了循环依赖<ul>
<li>一级缓存为单例池（singletonObjects）</li>
<li>二级缓存为早期曝光对象earlySingletonObjects</li>
<li>三级缓存为早期曝光对象工厂（singletonFactories）</li>
</ul>
</li>
<li>当A 、B两个类发生循环引用时，在A完成实例化后，就使用实例化后的对象去创建一个对象工厂，并添加到三级缓存中，如果A被AOP代理，那么通过这个工厂获取到的就是A代理后的对象，如果A没有被AOP代理，那么这个工厂获取到的就是A实例化的对象。当A进行属性注入时，会去创建B，同时B又依赖了A，所以创建B的同时又会去调用getBean(a)来获取需要的依赖，此时的getBean(a)会从缓存中获取<ul>
<li>第一步，先获取到三级缓存中的工厂；</li>
<li>第二步，调用对象工厂的getObject方法来获取到对应的对象，得到这个对象后将其注入到B中。紧接着B会走完它的生命周期流程，包括初始化、后置处理器等。当B创建完后，会将B再注入到A中，此时A再完成它的整个生命周期。至此，循环依赖结束<h2 id="1-9-详述BeanFactory和ApplicationContext"><a href="#1-9-详述BeanFactory和ApplicationContext" class="headerlink" title="1.9 详述BeanFactory和ApplicationContext"></a>1.9 详述BeanFactory和ApplicationContext</h2></li>
</ul>
</li>
<li>BeanFactory和ApplicationContext都是容器，BeanFactory 简单，可以理解为就是 HashMap，Key 是 BeanName，Value 是 Bean 实例。通常只提供注册（put），获取（get）这两个功能。可以称之为 “低级容器”。ApplicationContext 可以称之为 “高级容器”。因为它比 BeanFactory 多了更多的功能。它继承了多个接口。因此具备了更多的功能。例如资源的获取，支持多种消息（例如 JSP tag 的支持），对 BeanFactory 多了工具级别的支持等待。所以你看它的名字，已经不是 BeanFactory 之类的工厂了，而是 “应用上下文”， 代表着整个大容器的所有功能。该接口定义了一个 refresh 方法，此方法是所有阅读 Spring源码的人的最熟悉的方法，用于刷新整个容器，即重新加载/刷新所有的 bean。</li>
<li>IoC 在 Spring里，只需要低级容器就可以实现，2个步骤：<ul>
<li>1.加载配置文件，解析成 BeanDefinition 放在 Map 里。</li>
<li>2.调用 getBean 的时候，从 BeanDefinition 所属的 Map 里，拿出 Class 对象进行实例化，同时，如果有依赖关系，将递归调用 getBean 方法 —— 完成依赖注入。</li>
</ul>
</li>
<li>上面就是 Spring低级容器（BeanFactory）的 IoC。</li>
<li>至于高级容器 ApplicationContext，它包含了低级容器的功能，当它执行 refresh 模板方法的时候，将刷新整个容器的 Bean。同时其作为高级容器，包含了太多的功能。一句话，它不仅仅是 IoC。它支持不同信息源头，支持 BeanFactory 工具类，支持层级容器，支持访问文件资源，支持事件发布通知，支持接口回调等等。<h2 id="1-10-简述ApplicationContext的具体实现类"><a href="#1-10-简述ApplicationContext的具体实现类" class="headerlink" title="1.10 简述ApplicationContext的具体实现类"></a>1.10 简述ApplicationContext的具体实现类</h2></li>
<li><ol>
<li>FileSystemXmlApplicationContext ：此容器从一个XML文件中加载beans的定义，XML Bean 配置文件的全路径名必须提供给它的构造函数。</li>
</ol>
</li>
<li>2.ClassPathXmlApplicationContext：此容器也从一个XML文件中加载beans的定义，这里，你需要正确设置classpath因为这个容器将在classpath里找bean配置。</li>
<li>3.WebXmlApplicationContext：此容器加载一个XML文件，此文件定义了一个WEB应用的所有bean。<h2 id="1-11-简述Spring的依赖注入"><a href="#1-11-简述Spring的依赖注入" class="headerlink" title="1.11 简述Spring的依赖注入"></a>1.11 简述Spring的依赖注入</h2></li>
<li>控制反转IoC主要实现方式有两种：依赖注入和依赖查找<ul>
<li>依赖注入<ul>
<li>含义：相对于IoC而言，依赖注入(DI)更加准确地描述了IoC的设计理念。所谓依赖注入（Dependency Injection），即组件之间的依赖关系由容器在应用系统运行期来决定，也就是由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。组件不做定位查询，只提供普通的Java方法让容器去决定依赖关系。</li>
<li>1.11.1 依赖注入的基本原则<ul>
<li>依赖注入的基本原则是：应用组件不应该负责查找资源或者其他依赖的协作对象。配置对象的工作应该由IoC容器负责，“查找资源”的逻辑应该从应用组件的代码中抽取出来，交给IoC容器负责。容器全权负责组件的装配，它会把符合依赖关系的对象通过属性（JavaBean中的setter）或者是构造器传递给需要的对象。</li>
</ul>
</li>
<li>1.11.2 依赖注入有什么优势<ul>
<li>依赖注入之所以更流行是因为它是一种更可取的方式：让容器全权负责依赖查询，受管组件只需要暴露JavaBean的setter方法或者带参数的构造器或者接口，使容器可以在初始化时组装对象的依赖关系。</li>
</ul>
</li>
</ul>
</li>
<li>其与依赖查找方式相比，主要优势为：<ul>
<li>查找定位操作与应用代码完全无关。</li>
<li>不依赖于容器的API，可以很容易地在任何容器以外使用应用对象。</li>
<li>不需要特殊的接口，绝大多数对象可以做到完全不必依赖容器。</li>
<li>1.11.3 有哪些不同类型的依赖注入实现方式？<ul>
<li>依赖注入是时下最流行的IoC实现方式，依赖注入分为接口注入（Interface Injection），Setter方法注入（Setter Injection）和构造器注入（Constructor Injection）三种方式<ul>
<li>接口注入<ul>
<li>由于在灵活性和易用性比较差，现在从Spring4开始已被废弃。</li>
</ul>
</li>
<li>构造器依赖注入<ul>
<li>构造器依赖注入通过容器触发一个类的构造器来实现的，该类有一系列参数，每个参数代表一个对其他类的依赖。</li>
</ul>
</li>
<li>Setter方法注入<ul>
<li>Setter方法注入是容器通过调用无参构造器或无参static工厂 方法实例化bean之后，调用该bean的setter方法，即实现了基于setter的依赖注入。</li>
</ul>
</li>
</ul>
</li>
<li>6.2.4构造器依赖注入和 Setter方法注入的区别<ul>
<li>构造函数注入<ul>
<li>没有部分注入</li>
<li>不会覆盖 setter 属性</li>
<li>任意修改都会创建一个新实例</li>
<li>适用于设置很多属性</li>
</ul>
</li>
<li>setter 注入<ul>
<li>有部分注入</li>
<li>会覆盖 setter 属性</li>
<li>任意修改不会创建一个新实例</li>
<li>适用于设置少量属性<h2 id="1-12-简述Spring-beans"><a href="#1-12-简述Spring-beans" class="headerlink" title="1.12 简述Spring beans"></a>1.12 简述Spring beans</h2></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Spring beans 是那些形成Spring应用的主干的java对象。它们被SpringIOC容器初始化，装配和管理。这些beans通过容器中配置的元数据创建。比如，以XML文件中的形式定义<h2 id="1-13-简述Spring-Bean的作用域"><a href="#1-13-简述Spring-Bean的作用域" class="headerlink" title="1.13 简述Spring Bean的作用域"></a>1.13 简述Spring Bean的作用域</h2></li>
</ul>
</li>
<li>当定义一个 在Spring里，我们还能给这个bean声明一个作用域。它可以通过bean 定义中的scope属性来定义。如，当Spring要在需要的时候每次生产一个新的bean实例，bean的scope属性被指定为prototype。另一方面，一个bean每次使用的时候必须返回同一个实例，这个bean的scope 属性 必须设为Singleton。</li>
<li>Spring框架支持以下五种bean的作用域：<ul>
<li>singleton : bean在每个Springioc 容器中只有一个实例。</li>
<li>prototype：一个bean的定义可以有多个实例。</li>
<li>request：每次http请求都会创建一个bean，该作用域仅在基于web的SpringApplicationContext情形下有效。</li>
<li>session：在一个HTTP Session中，一个bean定义对应一个实例。该作用域仅在基于web的SpringApplicationContext情形下有效。</li>
<li>global-session：在一个全局的HTTP Session中，一个bean定义对应一个实例。该作用域仅在基于web的SpringApplicationContext情形下有效。</li>
</ul>
</li>
<li>注意： 缺省的Springbean 的作用域是Singleton。使用 prototype 作用域需要慎重的思考，因为频繁创建和销毁 bean 会带来很大的性能开销。<h2 id="1-14-简述Spring框架中的单例bean是线程安全？"><a href="#1-14-简述Spring框架中的单例bean是线程安全？" class="headerlink" title="1.14 简述Spring框架中的单例bean是线程安全？"></a>1.14 简述Spring框架中的单例bean是线程安全？</h2></li>
<li>不是，Spring框架中的单例bean不是线程安全的。</li>
<li>Spring中的 bean 默认是单例模式，Spring框架并没有对单例 bean 进行多线程的封装处理。</li>
<li>实际上大部分时候 Spring bean 无状态的（比如 dao 类），所有某种程度上来说 bean 也是安全的，但如果 bean 有状态的话（比如 view model 对象），那就要开发者自己去保证线程安全了，最简单的就是改变 bean 的作用域，把“Singleton”变更为“prototype”，这样请求 bean 相当于 new Bean()了，所以就可以保证线程安全了。</li>
<li>有状态就是有数据存储功能。</li>
<li>无状态就是不会保存数据。<h2 id="1-15-简述Spring如何处理线程并发问题"><a href="#1-15-简述Spring如何处理线程并发问题" class="headerlink" title="1.15 简述Spring如何处理线程并发问题"></a>1.15 简述Spring如何处理线程并发问题</h2></li>
<li>答：在一般情况下，只有无状态的Bean才可以在多线程环境下共享，在Spring中，绝大部分Bean都可以声明为Singleton作用域，因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理，解决线程安全问题。</li>
<li>ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式，仅提供一份变量，不同的线程在访问前需要获取锁，没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。    </li>
<li>ThreadLocal会为每一个线程提供一个独立的变量副本，从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本，从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象，在编写多线程代码时，可以把不安全的变量封装进ThreadLocal。<h2 id="1-16-简述Spring框架中bean的生命周期"><a href="#1-16-简述Spring框架中bean的生命周期" class="headerlink" title="1.16 简述Spring框架中bean的生命周期"></a>1.16 简述Spring框架中bean的生命周期</h2></li>
<li>在传统的Java应用中，bean的生命周期很简单。使用Java关键字new进行bean实例化，然后该bean就可以使用了。一旦该bean不再被使用，则由Java自动进行垃圾回收。相比之下，Spring容器中的bean的生命周期就显得相对复杂多了。正确理解Springbean的生命周期非常重要，因为你或许要利用Spring提供的扩展点来自定义bean的创建过程。</li>
<li>bean在Spring容器中从创建到销毁经历了若干阶段，每一阶段都可以针对Spring如何管理bean进行个性化定制。如上图，在bean准备就绪之前，bean工厂执行了若干启动步骤。进行详细描述：Spring对bean进行实例化；Spring将值和bean的引用注入到bean对应的属性中；如果bean实现了BeanNameAware接口，Spring将bean的ID传递给setBean-Name()方法；如果bean实现了BeanFactoryAware接口，Spring将调用setBeanFactory()方法，将BeanFactory容器实例传入；如果bean实现了ApplicationContextAware接口，Spring将调用setApplicationContext()方法，将bean所在的应用上下文的引用传入进来；如果bean实现了BeanPostProcessor接口，Spring将调用它们的post-ProcessBeforeInitialization()方法；</li>
<li>如果bean实现了InitializingBean接口，Spring将调用它们的after-PropertiesSet()方法。</li>
<li>类似地，如果bean使用initmethod声明了初始化方法，该方法也会被调用；</li>
<li>如果bean实现了BeanPostProcessor接口，Spring将调用它们的post-ProcessAfterInitialization()方法；</li>
<li>此时，bean已经准备就绪，可以被应用程序使用了，它们将一直驻留在应用上下文中，直到该应用上下文被销毁；</li>
<li>如果bean实现了DisposableBean接口，Spring将调用它的destroy()接口方法。同样，如果bean使用destroy-method声明了销毁方法，该方法也会被调用。</li>
<li>现在已经了解了如何创建和加载一个Spring容器。但是一个空的容器并没有太大的价值，在把东西放进去之前，它里面什么都没有。为了从Spring的DI(依赖注入)中受益，必须将应用对象装配进Spring容器中。<h2 id="1-17-简述重要的bean生命周期方法，能重载？"><a href="#1-17-简述重要的bean生命周期方法，能重载？" class="headerlink" title="1.17 简述重要的bean生命周期方法，能重载？"></a>1.17 简述重要的bean生命周期方法，能重载？</h2></li>
<li>有两个重要的bean 生命周期方法，第一个是setup ， 它是在容器加载bean的时候被调用。第二个方法是 teardown 它是在容器卸载类的时候被调用。</li>
<li>bean 标签有两个重要的属性（init-method和destroy-method）。用它们你可以自己定制初始化和注销方法。它们也有相应的注解（@PostConstruct和@PreDestroy）。<h2 id="1-18-简述Spring的内部bean"><a href="#1-18-简述Spring的内部bean" class="headerlink" title="1.18 简述Spring的内部bean"></a>1.18 简述Spring的内部bean</h2></li>
<li>答：在Spring框架中，当一个bean仅被用作另一个bean的属性时，它能被声明为一个内部bean。内部bean可以用setter注入“属性”和构造方法注入“构造参数”的方式来实现，内部bean通常是匿名的，它们的Scope一般是prototype。<h2 id="1-19-简述bean的自动装配？"><a href="#1-19-简述bean的自动装配？" class="headerlink" title="1.19 简述bean的自动装配？"></a>1.19 简述bean的自动装配？</h2></li>
<li>答：在Spring框架中，在配置文件中设定bean的依赖关系是一个很好的机制，Spring容器能够自动装配相互合作的bean，这意味着容器不需要和配置，能通过Bean工厂自动处理bean之间的协作。这意味着 Spring可以通过向Bean Factory中注入的方式自动搞定bean之间的依赖关系。自动装配可以设置在每个bean上，也可以设定在特定的bean上。<h2 id="1-20-简述Spring自动装配-bean方式"><a href="#1-20-简述Spring自动装配-bean方式" class="headerlink" title="1.20 简述Spring自动装配 bean方式"></a>1.20 简述Spring自动装配 bean方式</h2></li>
<li>答：在spring中，对象无需自己查找或创建与其关联的其他对象，由容器负责把需要相互协作的对象引用赋予各个对象，使用autowire来配置自动装载模式。</li>
<li>在Spring框架xml配置中共有5种自动装配：<ul>
<li>no：默认的方式是不进行自动装配的，通过手工设置ref属性来进行装配bean。</li>
<li>byName：通过bean的名称进行自动装配，如果一个bean的 property 与另一bean 的name 相同，就进行自动装配。</li>
<li>byType：通过参数的数据类型进行自动装配。</li>
<li>constructor：利用构造函数进行装配，并且构造函数的参数通过byType进行装配。</li>
<li>autodetect：自动探测，如果有构造方法，通过 construct的方式自动装配，否则使用 byType的方式自动装配。<h2 id="1-21-简述使用-Autowired注解自动装配的过程"><a href="#1-21-简述使用-Autowired注解自动装配的过程" class="headerlink" title="1.21 简述使用@Autowired注解自动装配的过程"></a>1.21 简述使用@Autowired注解自动装配的过程</h2></li>
</ul>
</li>
<li>答：使用@Autowired注解来自动装配指定的bean。在使用@Autowired注解之前需要在Spring配置文件进行配置，<context:annotation-config />。</li>
<li>在启动Spring IoC时，容器自动装载了一个AutowiredAnnotationBeanPostProcessor后置处理器，当容器扫描到@Autowied、@Resource或@Inject时，就会在IoC容器自动查找需要的bean，并装配给该对象的属性。在使用@Autowired时，首先在容器中查询对应类型的bean：</li>
<li>如果查询结果刚好为一个，就将该bean装配给@Autowired指定的数据；</li>
<li>如果查询的结果不止一个，那么@Autowired会根据名称来查找；</li>
<li>如果上述查找的结果为空，那么会抛出异常。解决方法时，使用required=false。<h2 id="1-22-简述自动装配有哪些局限性"><a href="#1-22-简述自动装配有哪些局限性" class="headerlink" title="1.22 简述自动装配有哪些局限性"></a>1.22 简述自动装配有哪些局限性</h2></li>
<li>自动装配的局限性是：<ul>
<li>重写：你仍需用和配置来定义依赖，意味着总要重写自动装配。</li>
<li>基本数据类型：你不能自动装配简单的属性，如基本数据类型，String字符串和类。</li>
<li>模糊特性：自动装配不如显式装配精确，如果有可能，建议使用显式装配。<h2 id="1-23-可以在Spring中注入一个null-和一个空字符串"><a href="#1-23-可以在Spring中注入一个null-和一个空字符串" class="headerlink" title="1.23 可以在Spring中注入一个null 和一个空字符串"></a>1.23 可以在Spring中注入一个null 和一个空字符串</h2></li>
</ul>
</li>
<li>可以。</li>
<li>@Component, @Controller, @Repository, @Service 有何区别？<ul>
<li>@Component：这将 java 类标记为 bean。它是任何 Spring管理组件的通用构造型。Spring的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。</li>
<li>@Controller：这将一个类标记为 SpringWeb MVC 控制器。标有它的 Bean 会自动导入到 IoC 容器中。</li>
<li>@Service：此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用 @Service 而不是 @Component，因为它以更好的方式指定了意图。</li>
<li>@Repository：这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IoC 容器，并使未经检查的异常有资格转换为 SpringDataAccessException。<h2 id="1-24-简述-Required-注解作用"><a href="#1-24-简述-Required-注解作用" class="headerlink" title="1.24 简述@Required 注解作用"></a>1.24 简述@Required 注解作用</h2></li>
</ul>
</li>
<li>这个注解表明bean的属性必须在配置的时候设置，通过一个bean定义的显式的属性值或通过自动装配，若@Required注解的bean属性未被设置，容器将抛出BeanInitializationException。<h2 id="1-25-简述-Autowired注解作用"><a href="#1-25-简述-Autowired注解作用" class="headerlink" title="1.25 简述@Autowired注解作用"></a>1.25 简述@Autowired注解作用</h2></li>
<li>@Autowired默认是按照类型装配注入的，默认情况下它要求依赖对象必须存在（可以设置它required属性为false）。@Autowired 注解提供了更细粒度的控制，包括在何处以及如何完成自动装配。它的用法和@Required一样，修饰setter方法、构造器、属性或者具有任意名称和/或多个参数的PN方法。<h2 id="1-26-简述-Autowired和-Resource的区别"><a href="#1-26-简述-Autowired和-Resource的区别" class="headerlink" title="1.26 简述@Autowired和@Resource的区别"></a>1.26 简述@Autowired和@Resource的区别</h2></li>
<li>@Autowired可用于：构造函数、成员变量、Setter方法</li>
<li>@Autowired和@Resource之间的区别<ul>
<li>@Autowired默认是按照类型装配注入的，默认情况下它要求依赖对象必须存在（可以设置它required属性为false）。</li>
<li>@Resource默认是按照名称来装配注入的，只有当找不到与名称匹配的bean才会按照类型来装配注入<h2 id="1-27-简述-Qualifier-注解作用"><a href="#1-27-简述-Qualifier-注解作用" class="headerlink" title="1.27 简述@Qualifier 注解作用"></a>1.27 简述@Qualifier 注解作用</h2></li>
</ul>
</li>
<li>创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时，您可以使用@Qualifier 注解和 @Autowired 通过指定应该装配哪个确切的 bean 来消除歧义。<h2 id="1-28-简述-RequestMapping-注解作用"><a href="#1-28-简述-RequestMapping-注解作用" class="headerlink" title="1.28 简述@RequestMapping 注解作用"></a>1.28 简述@RequestMapping 注解作用</h2></li>
<li>@RequestMapping 注解用于将特定 HTTP 请求方法映射到将处理相应请求的控制器中的特定类/方法。此注释可应用于两个级别：<ul>
<li>类级别：映射请求的 URL</li>
<li>方法级别：映射 URL 以及 HTTP 请求方法<h2 id="1-29-简述Spring支持Spring事务实现方式"><a href="#1-29-简述Spring支持Spring事务实现方式" class="headerlink" title="1.29 简述Spring支持Spring事务实现方式"></a>1.29 简述Spring支持Spring事务实现方式</h2></li>
</ul>
</li>
<li>编程式事务管理：这意味你通过编程的方式管理事务，给你带来极大的灵活性，但是难维护。</li>
<li>声明式事务管理：这意味着你可以将业务代码和事务管理分离，你只需用注解和XML配置来管理事务。<h2 id="1-30-简述Spring事务的实现方式及原理"><a href="#1-30-简述Spring事务的实现方式及原理" class="headerlink" title="1.30 简述Spring事务的实现方式及原理"></a>1.30 简述Spring事务的实现方式及原理</h2></li>
<li>Spring事务的本质其实就是数据库对事务的支持，没有数据库的事务支持，Spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。<h2 id="1-31-简述Spring的事务传播行为"><a href="#1-31-简述Spring的事务传播行为" class="headerlink" title="1.31 简述Spring的事务传播行为"></a>1.31 简述Spring的事务传播行为</h2></li>
<li>spring事务的传播行为说的是，当多个事务同时存在的时候，spring如何处理这些事务的行为。<ul>
<li>① PROPAGATION_REQUIRED：如果当前没有事务，就创建一个新事务，如果当前存在事务，就加入该事务，该设置是最常用的设置。</li>
<li>② PROPAGATION_SUPPORTS：支持当前事务，如果当前存在事务，就加入该事务，如果当前不存在事务，就以非事务执行。</li>
<li>③ PROPAGATION_MANDATORY：支持当前事务，如果当前存在事务，就加入该事务，如果当前不存在事务，就抛出异常。</li>
<li>④ PROPAGATION_REQUIRES_NEW：创建新事务，无论当前存不存在事务，都创建新事务。</li>
<li>⑤ PROPAGATION_NOT_SUPPORTED：以非事务方式执行操作，如果当前存在事务，就把当前事务挂起。</li>
<li>⑥ PROPAGATION_NEVER：以非事务方式执行，如果当前存在事务，则抛出异常。</li>
<li>⑦ PROPAGATION_NESTED：如果当前存在事务，则在嵌套事务内执行。如果当前没有事务，则按REQUIRED属性执行。<h2 id="1-32-简述Spring的事务隔离"><a href="#1-32-简述Spring的事务隔离" class="headerlink" title="1.32 简述Spring的事务隔离"></a>1.32 简述Spring的事务隔离</h2></li>
</ul>
</li>
<li>Spring有五大隔离级别，默认值为 ISOLATION_DEFAULT（使用数据库的设置），其他四个隔离级别和数据库的隔离级别一致：<ul>
<li>1.ISOLATION_DEFAULT：用底层数据库的设置隔离级别，数据库设置的是什么我就用什么；</li>
<li>2.ISOLATION_READ_UNCOMMITTED：未提交读，最低隔离级别、事务未提交前，就可被其他事务读取（会出现幻读、脏读、不可重复读）；</li>
<li>3.ISOLATION_READ_COMMITTED：提交读，一个事务提交后才能被其他事务读取到（会造成幻读、不可重复读），SQL server 的默认级别；</li>
<li>4.ISOLATION_REPEATABLE_READ：可重复读，保证多次读取同一个数据时，其值都和事务开始时候的内容是一致，禁止读取到别的事务未提交的数据（会造成幻读），MySQL 的默认级别；</li>
<li>5.ISOLATION_SERIALIZABLE：序列化，代价最高最可靠的隔离级别，该隔离级别能防止脏读、不可重复读、幻读。<ul>
<li>脏读 ：表示一个事务能够读取另一个事务中还未提交的数据。比如，某个事务尝试插入记录 A，此时该事务还未提交，然后另一个事务尝试读取到了记录 A。</li>
<li>不可重复读 ：是指在一个事务内，多次读同一数据。</li>
<li>幻读 ：指同一个事务内多次查询返回的结果集不一样。比如同一个事务 A 第一次查询时候有 n 条记录，但是第二次同等条件下查询却有 n+1 条记录，这就好像产生了幻觉。发生幻读的原因也是另外一个事务新增或者删除或者修改了第一个事务结果集里面的数据，同一个记录的数据内容被修改了，所有数据行的记录就变多或者变少了。<h2 id="1-33-简述Spring框架的事务管理优点"><a href="#1-33-简述Spring框架的事务管理优点" class="headerlink" title="1.33 简述Spring框架的事务管理优点"></a>1.33 简述Spring框架的事务管理优点</h2></li>
</ul>
</li>
</ul>
</li>
<li>为不同的事务API 如 JTA，JDBC，Hibernate，JPA 和JDO，提供一个不变的编程模式。</li>
<li>为编程式事务管理提供了一套简单的API而不是一些复杂的事务API</li>
<li>支持声明式事务管理。</li>
<li>和Spring各种数据访问抽象层很好得集成。<h2 id="1-34-简述事务管理类型"><a href="#1-34-简述事务管理类型" class="headerlink" title="1.34 简述事务管理类型"></a>1.34 简述事务管理类型</h2></li>
<li>答：大多数Spring框架的用户选择声明式事务管理，因为它对应用代码的影响最小，因此更符合一个无侵入的轻量级容器的思想。声明式事务管理要优于编程式事务管理，虽然比编程式事务管理（这种方式允许你通过代码控制事务）少了一点灵活性。唯一不足地方是，最细粒度只能作用到方法级别，无法做到像编程式事务那样可以作用到代码块级别。<h2 id="1-35-简述AOP"><a href="#1-35-简述AOP" class="headerlink" title="1.35 简述AOP"></a>1.35 简述AOP</h2></li>
<li>OOP(Object-Oriented Programming)面向对象编程，允许开发者定义纵向的关系，但并适用于定义横向的关系，导致了大量代码的重复，而不利于各个模块的重用。</li>
<li>AOP(Aspect-Oriented Programming)，一般称为面向切面编程，作为面向对象的一种补充，用于将那些与业务无关，但却对多个对象产生影响的公共行为和逻辑，抽取并封装为一个可重用的模块，这个模块被命名为“切面”（Aspect），减少系统中的重复代码，降低了模块间的耦合度，同时提高了系统的可维护性。可用于权限认证、日志、事务处理等。<h2 id="1-36-简述Spring-AOP和AspectJ-AOP"><a href="#1-36-简述Spring-AOP和AspectJ-AOP" class="headerlink" title="1.36 简述Spring AOP和AspectJ AOP"></a>1.36 简述Spring AOP和AspectJ AOP</h2></li>
<li>AOP实现的关键<ul>
<li>代理模式</li>
</ul>
</li>
<li>AOP代理主要分为<ul>
<li>静态代理<ul>
<li>静态代理的代表为AspectJ<ul>
<li>（1）AspectJ是静态代理的增强，所谓静态代理，就是AOP框架会在编译阶段生成AOP代理类，因此也称为编译时增强，他会在编译阶段将AspectJ(切面)织入到Java字节码中，运行的时候就是增强之后的AOP对象。</li>
</ul>
</li>
</ul>
</li>
<li>动态代理<ul>
<li>动态代理代表Spring AOP<ul>
<li>（2）Spring AOP使用的动态代理，所谓的动态代理就是说AOP框架不会去修改字节码，而是每次运行时在内存中临时为方法生成一个AOP对象，这个AOP对象包含了目标对象的全部方法，并且在特定的切点做了增强处理，并回调原对象的方法。<h2 id="1-37-简述JDK动态代理和CGLIB动态代理的区别"><a href="#1-37-简述JDK动态代理和CGLIB动态代理的区别" class="headerlink" title="1.37 简述JDK动态代理和CGLIB动态代理的区别"></a>1.37 简述JDK动态代理和CGLIB动态代理的区别</h2></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Spring AOP中的动态代理主要有两种方式，JDK动态代理和CGLIB动态代理：<ul>
<li>JDK动态代理只提供接口的代理，不支持类的代理。核心InvocationHandler接口和Proxy类，InvocationHandler 通过invoke()方法反射来调用目标类中的代码，动态地将横切逻辑和业务编织在一起；接着，Proxy利用 InvocationHandler动态创建一个符合某一接口的实例, 生成目标类的代理对象。<ul>
<li>如果代理类没有实现 InvocationHandler 接口，那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB（Code Generation Library），是一个代码生成的类库，可以在运行时动态的生成指定类的一个子类对象，并覆盖其中特定方法并添加增强代码，从而实现AOP。CGLIB是通过继承的方式做的动态代理，因此如果某个类被标记为final，那么它是无法使用CGLIB做动态代理的。</li>
</ul>
</li>
<li>静态代理与动态代理区别在于生成AOP代理对象的时机不同，相对来说AspectJ的静态代理方式具有更好的性能，但是AspectJ需要特定的编译器进行处理，而SpringAOP则无需特定的编译器处理。</li>
<li>InvocationHandler 的 invoke(Object proxy,Method method,Object[] args)：proxy是最终生成的代理实例; method 是被代理目标实例的某个具体方法; args 是被代理目标实例某个方法的具体入参, 在方法反射调用时使用。<h2 id="1-38-简述Spring中的代理"><a href="#1-38-简述Spring中的代理" class="headerlink" title="1.38 简述Spring中的代理"></a>1.38 简述Spring中的代理</h2></li>
</ul>
</li>
<li>答：将 Advice 应用于目标对象后创建的对象称为代理。在客户端对象的情况下，目标对象和代理对象是相同的。<h2 id="1-39-简述Spring-AOP名词"><a href="#1-39-简述Spring-AOP名词" class="headerlink" title="1.39 简述Spring AOP名词"></a>1.39 简述Spring AOP名词</h2></li>
<li>1）切面（Aspect）：切面是通知和切点的结合。通知和切点共同定义了切面的全部内容。 在SpringAOP中，切面可以使用通用类（基于模式的风格） 或者在普通类中以 @Aspect 注解来实现。</li>
<li>2）连接点（Join point）：指方法，在SpringAOP中，一个连接点 总是 代表一个方法的执行。 应用可能有数以千计的时机应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中，并添加新的行为。</li>
<li>3）通知（Advice）：在AOP术语中，切面的工作被称为通知。</li>
<li>4）切入点（Pointcut）：切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称，或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。</li>
<li>5）引入（Introduction）：引入允许我们向现有类添加新方法或属性。</li>
<li>6）目标对象（Target Object）： 被一个或者多个切面（aspect）所通知（advise）的对象。它通常是一个代理对象。也有人把它叫做 被通知（adviced） 对象。 既然SpringAOP是通过运行时代理实现的，这个对象永远是一个 被代理（proxied） 对象。</li>
<li>7）织入（Weaving）：织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有多少个点可以进行织入：<ul>
<li>编译期：切面在目标类编译时被织入。AspectJ的织入编译器是以这种方式织入切面的。</li>
<li>类加载期：切面在目标类加载到JVM时被织入。需要特殊的类加载器，它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入就支持以这种方式织入切面。</li>
<li>运行期：切面在应用运行的某个时刻被织入。一般情况下，在织入切面时，AOP容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面。<h2 id="1-40-简述Spring在运行时通知对象"><a href="#1-40-简述Spring在运行时通知对象" class="headerlink" title="1.40 简述Spring在运行时通知对象"></a>1.40 简述Spring在运行时通知对象</h2></li>
</ul>
</li>
<li>通过在代理类中包裹切面，Spring在运行期把切面织入到Spring管理的bean中。代理封装了目标类，并拦截被通知方法的调用，再把调用转发给真正的目标bean。当代理拦截到方法调用时，在调用目标bean方法之前，会执行切面逻辑。<h2 id="1-41-简述Spring只支持方法级别的连接点"><a href="#1-41-简述Spring只支持方法级别的连接点" class="headerlink" title="1.41 简述Spring只支持方法级别的连接点"></a>1.41 简述Spring只支持方法级别的连接点</h2></li>
<li>直到应用需要被代理的bean时，Spring才创建代理对象。如果使用的是ApplicationContext的话，在ApplicationContext从BeanFactory中加载所有bean的时候，Spring才会创建被代理的对象。因为Spring运行时才创建代理对象，所以我们不需要特殊的编译器来织入SpringAOP的切面。</li>
<li>因为Spring基于动态代理，所以Spring只支持方法连接点。Spring缺少对字段连接点的支持，而且它不支持构造器连接点。方法之外的连接点拦截功能，我们可以利用Aspect来补充。<h2 id="1-42-简述SpringAOP关注点和横切关注点的区别"><a href="#1-42-简述SpringAOP关注点和横切关注点的区别" class="headerlink" title="1.42 简述SpringAOP关注点和横切关注点的区别"></a>1.42 简述SpringAOP关注点和横切关注点的区别</h2></li>
<li>关注点（concern）是应用中一个模块的行为，一个关注点可能会被定义成一个想实现的一个功能。</li>
<li>横切关注点（cross-cutting concern）是一个关注点，此关注点是整个应用都会使用的功能，并影响整个应用，比如日志，安全等，几乎应用的每个模块都需要的功能。因此这些都属于横切关注点。<h2 id="1-43-Spring通知有哪些类型？"><a href="#1-43-Spring通知有哪些类型？" class="headerlink" title="1.43 Spring通知有哪些类型？"></a>1.43 Spring通知有哪些类型？</h2></li>
<li>在AOP术语中，切面的工作被称为通知，实际上是程序执行时要通过SpringAOP框架触发的代码段。</li>
<li>Spring切面可以应用5种类型的通知：<ul>
<li><ol>
<li>前置通知（Before）：在目标方法被调用之前调用通知功能；</li>
</ol>
</li>
<li>2.后置通知（After）：在目标方法完成之后调用通知，此时不会关心方法的输出是什么；</li>
<li>3.返回通知（After-returning ）：在目标方法成功执行之后调用通知；</li>
<li>4.异常通知（After-throwing）：在目标方法抛出异常后调用通知；</li>
<li>5.环绕通知（Around）：通知包裹了被通知的方法，在被通知的方法调用之前和调用之后执行自定义的行为。<ul>
<li>同一个aspect，不同advice的执行顺序：<ul>
<li>①没有异常情况下的执行顺序：<br>around before advice<br>before advice<br>target method 执行<br>around after advice<br>after advice<br>afterReturning</li>
<li>②有异常情况下的执行顺序：<br>around before advice<br>before advice<br>target method 执行<br>around after advice<br>after advice<br>afterThrowing:异常发生<br>java.lang.RuntimeException: 异常发生<h2 id="1-44-简述切面Aspect"><a href="#1-44-简述切面Aspect" class="headerlink" title="1.44 简述切面Aspect"></a>1.44 简述切面Aspect</h2></li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>aspect 由 pointcount 和 advice 组成，切面是通知和切点的结合。 它既包含了横切逻辑的定义, 也包括了连接点的定义. SpringAOP 就是负责实施切面的框架, 它将切面所定义的横切逻辑编织到切面所指定的连接点中.</li>
<li>AOP 的工作重心在于如何将增强编织目标对象的连接点上, 这里包含两个工作:<ul>
<li>如何通过 pointcut 和 advice 定位到特定的 joinpoint 上</li>
<li>如何在 advice 中编写切面代码.</li>
</ul>
</li>
<li>可以简单地认为, 使用 @Aspect 注解的类就是切面.<h2 id="1-45-简述Spring-MVC"><a href="#1-45-简述Spring-MVC" class="headerlink" title="1.45 简述Spring MVC"></a>1.45 简述Spring MVC</h2></li>
<li>SpringMVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架，通过把模型-视图-控制器分离，将web层进行职责解耦，把复杂的web应用分成逻辑清晰的几部分，简化开发，减少出错，方便组内开发人员之间的配合。<h2 id="1-46-简述Spring-MVC的优点"><a href="#1-46-简述Spring-MVC的优点" class="headerlink" title="1.46 简述Spring MVC的优点"></a>1.46 简述Spring MVC的优点</h2></li>
<li>（1）可以支持各种视图技术,而不仅仅局限于JSP；</li>
<li>（2）与Spring框架集成（如IoC容器、AOP等）；</li>
<li>（3）清晰的角色分配：前端控制器(dispatcherServlet) , 请求到处理器映射（handlerMapping), 处理器适配器（HandlerAdapter), 视图解析器（ViewResolver）。</li>
<li>（4） 支持各种请求资源的映射策略。<h2 id="1-47-简述Spring-MVC的主要组件"><a href="#1-47-简述Spring-MVC的主要组件" class="headerlink" title="1.47 简述Spring MVC的主要组件"></a>1.47 简述Spring MVC的主要组件</h2></li>
<li>（1）前端控制器 DispatcherServlet（不需要程序员开发）<ul>
<li>作用：接收请求、响应结果，相当于转发器，有了DispatcherServlet 就减少了其它组件之间的耦合度。</li>
</ul>
</li>
<li>（2）处理器映射器HandlerMapping（不需要程序员开发）<ul>
<li>作用：根据请求的URL来查找Handler</li>
</ul>
</li>
<li>（3）处理器适配器HandlerAdapter<ul>
<li>注意：在编写Handler的时候要按照HandlerAdapter要求的规则去编写，这样适配器HandlerAdapter才可以正确的去执行Handler。</li>
</ul>
</li>
<li>（4）处理器Handler（需要程序员开发）</li>
<li>（5）视图解析器 ViewResolver（不需要程序员开发）<ul>
<li>作用：进行视图的解析，根据视图逻辑名解析成真正的视图（view）</li>
</ul>
</li>
<li>（6）视图View（需要程序员开发jsp）<ul>
<li>View是一个接口， 它的实现类支持不同的视图类型（jsp，freemarker，pdf等等）<h2 id="1-48-简述DispatcherServlet"><a href="#1-48-简述DispatcherServlet" class="headerlink" title="1.48 简述DispatcherServlet"></a>1.48 简述DispatcherServlet</h2></li>
</ul>
</li>
<li>Spring的MVC框架是围绕DispatcherServlet来设计的，它用来处理所有的HTTP请求和响应。<h2 id="1-49-简述是Spring-MVC框架的控制器"><a href="#1-49-简述是Spring-MVC框架的控制器" class="headerlink" title="1.49 简述是Spring MVC框架的控制器"></a>1.49 简述是Spring MVC框架的控制器</h2></li>
<li>控制器提供一个访问应用程序的行为，此行为通常通过服务接口实现。</li>
<li>控制器解析用户输入并将其转换为一个由视图呈现给用户的模型。</li>
<li>Spring用一个非常抽象的方式实现了一个控制层，允许用户创建多种用途的控制器。<h2 id="1-50-简述Spring-MVC的控制器"><a href="#1-50-简述Spring-MVC的控制器" class="headerlink" title="1.50 简述Spring MVC的控制器"></a>1.50 简述Spring MVC的控制器</h2></li>
<li>控制器是单例模式,所以在多线程访问的时候有线程安全问题,不要用同步,会影响性能的。<h2 id="1-51-简述Spring-MVC的工作流程"><a href="#1-51-简述Spring-MVC的工作流程" class="headerlink" title="1.51 简述Spring MVC的工作流程"></a>1.51 简述Spring MVC的工作流程</h2></li>
<li>（1）用户发送请求至前端控制器DispatcherServlet；</li>
<li>（2） DispatcherServlet收到请求后，调用HandlerMapping处理器映射器，请求获取Handle；</li>
<li>（3）处理器映射器根据请求url找到具体的处理器，生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet；</li>
<li>（4）DispatcherServlet 调用 HandlerAdapter处理器适配器；</li>
<li>（5）HandlerAdapter 经过适配调用 具体处理器(Handler，也叫后端控制器)；</li>
<li>（6）Handler执行完成返回ModelAndView；</li>
<li>（7）HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet；</li>
<li>（8）DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析；</li>
<li>（9）ViewResolver解析后返回具体View；</li>
<li>（10）DispatcherServlet对View进行渲染视图（即将模型数据填充至视图中）</li>
<li>（11）DispatcherServlet响应用户。<h2 id="1-52-简述Spring-MVC"><a href="#1-52-简述Spring-MVC" class="headerlink" title="1.52 简述Spring MVC"></a>1.52 简述Spring MVC</h2></li>
<li>mvc是一种设计模式（设计模式就是日常开发中编写代码的一种好的方法和经验的总结）。<ul>
<li>三层架构的设计模式:模型（model）-视图（view）-控制器（controller）用于实现前端页面的展现与后端业务数据处理的分离。</li>
</ul>
</li>
<li>mvc设计模式的好处：<ul>
<li>1.分层设计，实现了业务系统各个组件之间的解耦，有利于业务系统的可扩展性，可维护性。</li>
<li>2.有利于系统的并行开发，提升开发效率。<h2 id="1-53-简述注解原理"><a href="#1-53-简述注解原理" class="headerlink" title="1.53 简述注解原理"></a>1.53 简述注解原理</h2></li>
</ul>
</li>
<li>注解本质是一个继承了Annotation的特殊接口，其具体实现类是Java运行时生成的动态代理类。我们通过反射获取注解时，返回的是Java运行时生成的动态代理对象。通过代理对象调用自定义注解的方法，会最终调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池。<h2 id="1-54-简述Spring-MVC常用的注解有哪些？"><a href="#1-54-简述Spring-MVC常用的注解有哪些？" class="headerlink" title="1.54 简述Spring MVC常用的注解有哪些？"></a>1.54 简述Spring MVC常用的注解有哪些？</h2></li>
<li>@RequestMapping：用于处理请求 url 映射的注解，可用于类或方法上。用于类上，则表示类中的所有响应请求的方法都是以该地址作为父路径。</li>
<li>@RequestBody：注解实现接收http请求的json数据，将json转换为java对象。</li>
<li>@ResponseBody：注解实现将conreoller方法返回对象转化为json对象响应给客户。<h2 id="1-55-简述Sping-MVC中的控制器的注解"><a href="#1-55-简述Sping-MVC中的控制器的注解" class="headerlink" title="1.55 简述Sping MVC中的控制器的注解"></a>1.55 简述Sping MVC中的控制器的注解</h2></li>
<li>一般用@Controller注解,也可以使用@RestController,@RestController注解相当于@ResponseBody＋@Controller,表示是表现层,除此之外，一般不用别的注解代替。<h2 id="1-56-简述-Controller注解的作用"><a href="#1-56-简述-Controller注解的作用" class="headerlink" title="1.56 简述@Controller注解的作用"></a>1.56 简述@Controller注解的作用</h2></li>
<li>在SpringMVC 中，控制器Controller 负责处理由DispatcherServlet 分发的请求，它把用户请求的数据经过业务处理层处理之后封装成一个Model ，然后再把该Model 返回给对应的View 进行展示。在SpringMVC 中提供了一个非常简便的定义Controller 的方法，你无需继承特定的类或实现特定的接口，只需使用@Controller 标记一个类是Controller ，然后使用@RequestMapping 和@RequestParam 等一些注解用以定义URL 请求和Controller 方法之间的映射，这样的Controller 就能被外界访问到。此外Controller 不会直接依赖于HttpServletRequest 和HttpServletResponse 等HttpServlet 对象，它们可以通过Controller 的方法参数灵活的获取到。</li>
<li>@Controller 用于标记在一个类上，使用它标记的类就是一个SpringMVC Controller 对象。分发处理器将会扫描使用了该注解的类的方法，并检测该方法是否使用了@RequestMapping 注解。@Controller 只是定义了一个控制器类，而使用@RequestMapping 注解的方法才是真正处理请求的处理器。单单使用@Controller 标记在一个类上还不能真正意义上的说它就是SpringMVC 的一个控制器类，因为这个时候Spring还不认识它。那么要如何做Spring才能认识它呢？这个时候就需要我们把这个控制器类交给Spring来管理。有两种方式：</li>
<li>在SpringMVC 的配置文件中定义MyController 的bean 对象。</li>
<li>在SpringMVC 的配置文件中告诉Spring该到哪里去找标记为@Controller的Controller 控制器。<h2 id="1-57-简述-RequestMapping注解的作用"><a href="#1-57-简述-RequestMapping注解的作用" class="headerlink" title="1.57 简述@RequestMapping注解的作用"></a>1.57 简述@RequestMapping注解的作用</h2></li>
<li>RequestMapping是一个用来处理请求地址映射的注解，可用于类或方法上。用于类上，表示类中的所有响应请求的方法都是以该地址作为父路径。</li>
<li>RequestMapping注解有六个属性，下面我们把她分成三类进行说明（下面有相应示例）。<ul>
<li>value， method<ul>
<li>value： 指定请求的实际地址，指定的地址可以是URI Template 模式（后面将会说明）；</li>
<li>method： 指定请求的method类型， GET、POST、PUT、DELETE等；</li>
</ul>
</li>
<li>consumes，produces<ul>
<li>consumes： 指定处理请求的提交内容类型（Content-Type），例如application/json, text/html;</li>
<li>produces: 指定返回的内容类型，仅当request请求头中的(Accept)类型中包含该指定类型才返回；</li>
</ul>
</li>
<li>params，headers<ul>
<li>params： 指定request中必须包含某些参数值是，才让该方法处理。</li>
<li>headers： 指定request中必须包含某些指定的header值，才能让该方法处理请求。<h2 id="1-58-简述-ResponseBody注解的作用"><a href="#1-58-简述-ResponseBody注解的作用" class="headerlink" title="1.58 简述@ResponseBody注解的作用"></a>1.58 简述@ResponseBody注解的作用</h2></li>
</ul>
</li>
</ul>
</li>
<li>该注解用于将Controller的方法返回的对象，通过适当的HttpMessageConverter转换为指定格式后，写入到Response对象的body数据区。使用时机：返回的数据不是html标签的页面，而是其他某种格式的数据时（如json、xml等）使用；<h2 id="1-59-简述-PathVariable和-RequestParam的区别"><a href="#1-59-简述-PathVariable和-RequestParam的区别" class="headerlink" title="1.59 简述@PathVariable和@RequestParam的区别"></a>1.59 简述@PathVariable和@RequestParam的区别</h2></li>
<li>请求路径上有个id的变量值，可以通过@PathVariable来获取 @RequestMapping(value = “/page/{id}”, method = RequestMethod.GET)；@RequestParam用来获得静态的URL请求入参 spring注解时action里用到。<h2 id="1-60-简述Spring-MVC与Struts2区别"><a href="#1-60-简述Spring-MVC与Struts2区别" class="headerlink" title="1.60 简述Spring MVC与Struts2区别"></a>1.60 简述Spring MVC与Struts2区别</h2></li>
<li>相同点：<ul>
<li>都是基于mvc的表现层框架，都用于web项目的开发。</li>
</ul>
</li>
<li>不同点：<ul>
<li>1.前端控制器不一样。Spring MVC的前端控制器是Servlet：DispatcherServlet。struts2的前端控制器是filter：StrutsPreparedAndExcutorFilter。</li>
<li>2.请求参数的接收方式不一样。Spring MVC是使用方法的形参接收请求的参数，基于方法的开发，线程安全，可以设计为单例或者多例的开发，推荐使用单例模式的开发（执行效率更高），默认就是单例开发模式。struts2是通过类的成员变量接收请求的参数，是基于类的开发，线程不安全，只能设计为多例的开发。</li>
<li>3.Struts采用值栈存储请求和响应的数据，通过OGNL存取数据，Spring MVC通过参数解析器是将request请求内容解析，并给方法形参赋值，将数据和视图封装成ModelAndView对象，最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp视图解析器默认使用jstl。</li>
<li>4.与Spring整合不一样。Spring MVC是Spring框架的一部分，不需要整合。在企业项目中，Spring MVC使用更多一些。<h2 id="1-61-简述SpringMVC设定重定向和转发"><a href="#1-61-简述SpringMVC设定重定向和转发" class="headerlink" title="1.61 简述SpringMVC设定重定向和转发"></a>1.61 简述SpringMVC设定重定向和转发</h2></li>
</ul>
</li>
<li>（1）转发：在返回值前面加”forward:”，譬如”forward:index “</li>
<li>（2）重定向：在返回值前面加”redirect:”，譬如”redirect:<a target="_blank" rel="noopener" href="http://www.xunhe.com">http://www.xunhe.com</a>“<ul>
<li>重定向(Redirect)就是通过各种方法将各种网络请求重新定个方向转到其它位置<h2 id="1-62-Spring-MVC怎么和AJAX相互调用的"><a href="#1-62-Spring-MVC怎么和AJAX相互调用的" class="headerlink" title="1.62 Spring MVC怎么和AJAX相互调用的"></a>1.62 Spring MVC怎么和AJAX相互调用的</h2></li>
</ul>
</li>
<li>通过Jackson框架就可以把Java里面的对象直接转化成Js可以识别的Json对象。具体步骤如下 ：<ul>
<li>（1）加入Jackson.jar</li>
<li>（2）在配置文件中配置json的映射</li>
<li>（3）在接受Ajax方法里面可以直接返回Object,List等,但方法前面要加上@ResponseBody注解。<h2 id="1-63-如何解决POST请求中文乱码问题"><a href="#1-63-如何解决POST请求中文乱码问题" class="headerlink" title="1.63 如何解决POST请求中文乱码问题"></a>1.63 如何解决POST请求中文乱码问题</h2></li>
</ul>
</li>
<li>解决post请求乱码问题：<ul>
<li>在web.xml中配置一个CharacterEncodingFilter过滤器，设置成utf-8<h2 id="1-64-简述SpringMVC的异常处理"><a href="#1-64-简述SpringMVC的异常处理" class="headerlink" title="1.64 简述SpringMVC的异常处理"></a>1.64 简述SpringMVC的异常处理</h2></li>
</ul>
</li>
<li>可以将异常抛给Spring框架，由Spring框架来处理；只需要配置简单的异常处理器，在异常处理器中添视图页面即可<h2 id="1-65-简述如何拦截Get方式提交的方法"><a href="#1-65-简述如何拦截Get方式提交的方法" class="headerlink" title="1.65 简述如何拦截Get方式提交的方法"></a>1.65 简述如何拦截Get方式提交的方法</h2></li>
<li>可以在@RequestMapping注解里面加上method=RequestMethod.GET<h2 id="1-66-简述在方法里面如何得到Request"><a href="#1-66-简述在方法里面如何得到Request" class="headerlink" title="1.66 简述在方法里面如何得到Request"></a>1.66 简述在方法里面如何得到Request</h2></li>
<li>直接在方法的形参中声明request,Spring MVC就自动把request对象传入<h2 id="1-67-简述Spring-MVC中函数的返回值"><a href="#1-67-简述Spring-MVC中函数的返回值" class="headerlink" title="1.67 简述Spring MVC中函数的返回值"></a>1.67 简述Spring MVC中函数的返回值</h2></li>
<li>返回值可以有很多类型有：String, ModelAndView。ModelAndView类把视图和数据都合并的一起的，但一般用String比较好<h2 id="1-68-Spring-MVC用何对象从后台向前台传递数据"><a href="#1-68-Spring-MVC用何对象从后台向前台传递数据" class="headerlink" title="1.68 Spring MVC用何对象从后台向前台传递数据"></a>1.68 Spring MVC用何对象从后台向前台传递数据</h2></li>
<li>通过ModelMap对象,可以在这个对象里面调用put方法,把对象加到里面,前台就可以通过el表达式拿到<h2 id="1-69-如何实现Spring-MVC拦截器"><a href="#1-69-如何实现Spring-MVC拦截器" class="headerlink" title="1.69 如何实现Spring MVC拦截器"></a>1.69 如何实现Spring MVC拦截器</h2></li>
<li>一种是实现HandlerInterceptor接口</li>
<li>另外一种是继承适配器类，接着在接口方法当中，实现处理逻辑；然后在Spring MVC的配置文件中配置拦截器即可：</li>
<li>WebApplicationContext 继承了ApplicationContext 并增加了一些WEB应用必备的特有功能，它不同于一般的ApplicationContext ，因为它能处理主题，并找到被关联的Servlet。<h2 id="1-70-简述-Transactional失效场景"><a href="#1-70-简述-Transactional失效场景" class="headerlink" title="1.70 简述@Transactional失效场景"></a>1.70 简述@Transactional失效场景</h2></li>
<li>1.@Transactional注解标注方法修饰符为非public时，@Transactional注解将会不起作用。</li>
<li>2.在类内部调用类内部@Transactional标注的方法。这种情况下也会导致事务不开启；</li>
<li>3.事务方法内部捕捉了异常，没有抛出新的异常，导致事务操作不会进行回滚。</li>
</ul>

      
    </div>
    <footer class="article-footer">
      <a data-url="https://goofyer.gitee.io/notes-on-computer-expertise/2022/01/02/Spring%E5%BC%80%E5%8F%91/%E4%B8%80%E3%80%81spring%E5%9F%BA%E7%A1%80%E4%BB%8B%E7%BB%8D/" data-id="cl403sxqr000of8vufzvt7oph" data-title="一、spring基础介绍" class="article-share-link">Share</a>
      
      
      
    </footer>
  </div>
  
    
<nav id="article-nav">
  
    <a href="/notes-on-computer-expertise/2022/01/02/2-1-8-%E8%BF%9B%E7%A8%8B%E7%9A%84%E4%B9%A0%E9%A2%98/" id="article-nav-newer" class="article-nav-link-wrap">
      <strong class="article-nav-caption">Newer</strong>
      <div class="article-nav-title">
        
          2.1.8 进程的习题
        
      </div>
    </a>
  
  
    <a href="/notes-on-computer-expertise/2022/01/02/vba%E5%9F%BA%E7%A1%80%E6%93%8D%E4%BD%9C%E7%AC%94%E8%AE%B0/vba%E5%9F%BA%E7%A1%80%E6%93%8D%E4%BD%9C/" id="article-nav-older" class="article-nav-link-wrap">
      <strong class="article-nav-caption">Older</strong>
      <div class="article-nav-title">vba基础操作</div>
    </a>
  
</nav>

  
</article>


</section>
        
          <aside id="sidebar">
  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Categories</h3>
    <div class="widget">
      <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/C-%E5%B7%A5%E5%85%B7/">C#工具</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/C-%E5%B7%A5%E5%85%B7/">C++工具</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/Mysql/">Mysql</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/blender/">blender</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/linux/">linux</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/manim/">manim</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/spring/">spring</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/vba%E5%9F%BA%E7%A1%80%E6%93%8D%E4%BD%9C/">vba基础操作</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E5%88%9B%E9%80%A0%E6%A8%A1%E5%BC%8F/">创造模式</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/">操作系统</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/">操作系统基础知识</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F%E5%BC%80%E5%8F%91/">操作系统开发</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%95%B0%E5%AD%A6/">数学</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/">数据结构</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/">数据结构基础知识</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%A8%A1%E6%9D%BF/">模板</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E6%B7%B1%E5%BA%A6%E5%AD%A6%E4%B9%A0/">深度学习</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E8%8B%B1%E8%AF%AD/">英语</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E8%99%9A%E5%B9%BB4/">虚幻4</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BB%84%E6%88%90%E5%8E%9F%E7%90%86/">计算机组成原理</a></li><li class="category-list-item"><a class="category-list-link" href="/notes-on-computer-expertise/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/">计算机网络</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tags</h3>
    <div class="widget">
      <ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/notes-on-computer-expertise/tags/C/" rel="tag">C++</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Tag Cloud</h3>
    <div class="widget tagcloud">
      <a href="/notes-on-computer-expertise/tags/C/" style="font-size: 10px;">C++</a>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Archives</h3>
    <div class="widget">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/notes-on-computer-expertise/archives/2022/05/">May 2022</a></li><li class="archive-list-item"><a class="archive-list-link" href="/notes-on-computer-expertise/archives/2022/03/">March 2022</a></li><li class="archive-list-item"><a class="archive-list-link" href="/notes-on-computer-expertise/archives/2022/02/">February 2022</a></li><li class="archive-list-item"><a class="archive-list-link" href="/notes-on-computer-expertise/archives/2022/01/">January 2022</a></li><li class="archive-list-item"><a class="archive-list-link" href="/notes-on-computer-expertise/archives/2021/12/">December 2021</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">Recent Posts</h3>
    <div class="widget">
      <ul>
        
          <li>
            <a href="/notes-on-computer-expertise/2022/05/31/vue/%E5%89%8D%E7%AB%AF%E6%A1%86%E6%9E%B6%E9%AA%8C%E8%AF%81%E7%A0%81%E6%97%A0%E6%B3%95%E6%98%BE%E7%A4%BA/">(no title)</a>
          </li>
        
          <li>
            <a href="/notes-on-computer-expertise/2022/05/26/%E6%95%B0%E6%8D%AE%E5%BA%93/navicat%E5%AE%89%E8%A3%85/">(no title)</a>
          </li>
        
          <li>
            <a href="/notes-on-computer-expertise/2022/05/25/%E8%99%9A%E5%B9%BB4%E5%BC%95%E6%93%8E%E5%BC%80%E5%8F%91/%E8%99%9A%E5%B9%BB%E5%9B%9B%20mod%E5%88%B6%E4%BD%9C/">(no title)</a>
          </li>
        
          <li>
            <a href="/notes-on-computer-expertise/2022/05/23/python/python%E6%89%B9%E9%87%8F%E7%94%9F%E6%88%90%E6%95%B0%E6%8D%AE/">(no title)</a>
          </li>
        
          <li>
            <a href="/notes-on-computer-expertise/2022/05/23/vba%E5%9F%BA%E7%A1%80%E6%93%8D%E4%BD%9C%E7%AC%94%E8%AE%B0/EXCEL%E5%9F%BA%E7%A1%80%E6%95%99%E7%A8%8B/">(no title)</a>
          </li>
        
      </ul>
    </div>
  </div>

  
</aside>
        
      </div>
      <footer id="footer">
  
  <div class="outer">
    <div id="footer-info" class="inner">
      
      &copy; 2022 John Doe<br>
      Powered by <a href="https://hexo.io/" target="_blank">Hexo</a>
    </div>
  </div>
</footer>

    </div>
    <nav id="mobile-nav">
  
    <a href="/notes-on-computer-expertise/" class="mobile-nav-link">Home</a>
  
    <a href="/notes-on-computer-expertise/archives" class="mobile-nav-link">Archives</a>
  
</nav>
    


<script src="/notes-on-computer-expertise/js/jquery-3.4.1.min.js"></script>



  
<script src="/notes-on-computer-expertise/fancybox/jquery.fancybox.min.js"></script>




<script src="/notes-on-computer-expertise/js/script.js"></script>





  </div>
<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
        tex2jax: {
            inlineMath: [ ["$","$"], ["\\(","\\)"] ],
            skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'],
            processEscapes: true
        }
    });
    MathJax.Hub.Queue(function() {
        var all = MathJax.Hub.getAllJax();
        for (var i = 0; i < all.length; ++i)
            all[i].SourceElement().parentNode.className += ' has-jax';
    });
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_CHTML"></script>
</body>
</html>